///************************************************************************
/// <copyrigth>2018-2019 Corporation.All Rights Reserved</copyrigth>
/// <author>tangyingzhong</author>
/// <contact>tangyz114987@outlook.com</contact>
/// <version>v1.0.0</version>
/// <describe>
/// Decode video with hardware
///</describe>
/// <date>2021/6/26</date>
///***********************************************************************
#ifndef VIDEOD3DRENDER_H
#define VIDEOD3DRENDER_H

#include "IVideoD3dRender.h"

struct D3dFormat
{
	// Format name
	std::string strName;

	// Format
	D3DFORMAT Direct3DFormat;

	// Pixel format
	AVPixelFormat PixelFormat;
};

// Dxva2 mode
struct Dxva2Mode 
{
	// Guid
	const GUID* pGuid;

	// Avcode Id
	AVCodecID CodecId;
};

// Direct3D surface info
struct SurfaceInfo
{
	// Is used surface
	int iUsed;

	// Age of it
	uint64_t iAge;
};

// Dxva2 Context
struct Dxva2Context 
{
	// D3d library handle
	HMODULE hD3dLib;

	// Dxva2 library handle
	HMODULE hDxva2Lib;

	// D3d device handle
	HANDLE hD3dDevice;

	// Direct3D
	IDirect3D9* pD3d;

	// Direct3D device
	IDirect3DDevice9* pD3dDevice;

	// Direct3D device manager
	IDirect3DDeviceManager9* pD3dDeviceManager;

	// Direct video decoder service
	IDirectXVideoDecoderService* pDirectVideoDecoderService;

	// Direct video decoder
	IDirectXVideoDecoder* pDirectVideoDecoder;

	// Decoder guid
	GUID DecoderGuid;

	// Dxva2 configure picture decode
	DXVA2_ConfigPictureDecode Dxva2DecoderConfig;

	// Direct3D surface
	LPDIRECT3DSURFACE9* pSurfaces;

	// Surface info
	SurfaceInfo* pSurefaceInfo;

	// Number of the surfaces
	uint32_t SurfaceNum;

	// Surface's age
	uint64_t SurfaceAge;

	// Temp frame
	AVFrame* pTempFrame;
};

// Dxva2 surface wrapper
struct Dxva2SurfaceWrapper
{
	// Dxva2 context
	Dxva2Context* pDxva2Context;

	// Direct3D surface
	LPDIRECT3DSURFACE9 CurSurface;

	// Direct video decoder
	IDirectXVideoDecoder* pDirectVideoDecoder;
};

// Direct3D present
struct D3dPresent
{
	// Direct3D present para
	D3DPRESENT_PARAMETERS D3dPresentPara = { 0 };

	// View port of your UI
	RECT ViewPort;

	// Direct3D render surface
	IDirect3DSurface9* pD3dRenderSurface = NULL;

	// Direct3D back buffer
	IDirect3DSurface9* pBackBuffer = NULL;
};

class VideoD3dRender :public IVideoD3dRender
{
public:
	typedef IDirect3D9* WINAPI pDirect3DCreate9(UINT);

	typedef HRESULT WINAPI pCreateDeviceManager9(UINT*, IDirect3DDeviceManager9**);

public:
	// Construct the VideoD3dRender
	VideoD3dRender();

	// Detructe the VideoD3dRender
	virtual ~VideoD3dRender();

private:
	// Forbid the copy VideoD3dRender
	VideoD3dRender(const VideoD3dRender& other) {	};

	// Forbid the assigment of VideoD3dRender
	VideoD3dRender& operator=(const VideoD3dRender& other) { return *this; };

public:
	// Configure the render
	virtual bool Init(AVCodecContext* pAvctx, void* pWindowHandle);

	// Render the data
	virtual bool Render(AVFrame* pFrame);
	
private:
	// Get hard pixel format
	AVPixelFormat GetHardPixelFormat(AVCodecContext* pAvContext, const enum AVPixelFormat* pPixelFormat);

	// Release buffer
	static void Dxva2ReleaseBuffer(void* pUserData, uint8_t* data);

	// Get buffer 
	static int Dxva2GetBuffer(AVCodecContext* pAvContext, AVFrame* pFrame, int iFlags);

	// Retrieve data
	static int Dxva2RetrieveData(AVCodecContext* pAvContext, AVFrame* pFrame);

private:
	// Initialize the decoder
	void Initialize();

	// Destory the decoder
	void Destory();

	// Release D3d context
	void ReleaseD3dContext();

	// Release render
	void ReleaseRender();

private:
	// Get the WinHandle
	inline void* GetWinHandle() const
	{
		return m_pWinHandle;
	}

	// Set the WinHandle
	inline void SetWinHandle(void* pWinHandle)
	{
		m_pWinHandle = pWinHandle;
	}

	// Get the DecCodecCtx
	inline AVCodecContext* GetDecCodecCtx() const
	{
		return m_pDecCodecCtx;
	}

	// Set the DecCodecCtx
	inline void SetDecCodecCtx(AVCodecContext* pDecCodecCtx)
	{
		m_pDecCodecCtx = pDecCodecCtx;
	}

	// Get the IsHaveInitRender
	inline bool GetIsHaveInitRender() const
	{
		return m_bIsHaveInitRender;
	}

	// Set the IsHaveInitRender
	inline void SetIsHaveInitRender(bool bIsHaveInitRender)
	{
		m_bIsHaveInitRender = bIsHaveInitRender;
	}

	// Get the disposed status
	inline bool GetDisposed() const
	{
		return m_bDisposed;
	}

	// Set the disposed status
	inline void SetDisposed(bool bDisposed)
	{
		m_bDisposed = bDisposed;
	}

private:
	// Direct3D present
	D3dPresent m_D3dPresent;

	// Window handle
	void* m_pWinHandle;
	
	// Decode context
	AVCodecContext* m_pDecCodecCtx;

	// Is init the render
	bool m_bIsHaveInitRender;

	// Section Lock
	CRITICAL_SECTION m_SectionLock;

	// Disposed status
	bool m_bDisposed;
};

#endif // VIDEOD3DRENDER_H
